1 module hip.hipaudio.backend.xaudio.source; 2 3 version(Windows): 4 version(DirectX): 5 6 import hip.hipaudio.backend.xaudio.player; 7 import hip.hipaudio.backend.xaudio.clip; 8 import hip.hipaudio.audiosource; 9 import directx.xaudio2; 10 import directx.win32; 11 import hip.util.system:getWindowsErrorMessage; 12 import hip.error.handler; 13 14 enum WAVE_FORMAT_IEEE_FLOAT = 0x0003; 15 16 class HipXAudioSource : HipAudioSource 17 { 18 IXAudio2SourceVoice sourceVoice; 19 protected bool isClipDirty = true; 20 21 this(HipXAudioPlayer player) 22 { 23 24 AudioConfig cfg = player.config; 25 WAVEFORMATEX fmt; 26 WORD format; 27 28 switch(cfg.getBitDepth) 29 { 30 case 8: 31 case 16: 32 format = WAVE_FORMAT_PCM; 33 break; 34 case 32: 35 format = WAVE_FORMAT_IEEE_FLOAT; 36 break; 37 default: 38 ErrorHandler.assertExit(false, "XAudio2 Does not support the current bit depth"); 39 break; 40 } 41 fmt.wFormatTag = format; 42 fmt.nChannels = cast(ushort)cfg.channels; 43 fmt.nAvgBytesPerSec = cfg.sampleRate * (cfg.getBitDepth/8) * cfg.channels; 44 fmt.nSamplesPerSec = cfg.sampleRate; 45 fmt.nBlockAlign = cast(ushort)(cfg.channels * cfg.getBitDepth())/8; 46 fmt.wBitsPerSample = cast(ushort)cfg.getBitDepth; 47 fmt.cbSize = 0; 48 HRESULT hr = player.xAudio.CreateSourceVoice(&sourceVoice, &fmt); 49 50 ErrorHandler.assertLazyExit(SUCCEEDED(hr), "Could not create source voice: \n\t"~HipXAudioPlayer.getError(hr)); 51 52 } 53 alias clip = HipAudioSource.clip; 54 55 56 protected void submitClip() 57 { 58 XAUDIO2_BUFFER* buffer = getBufferFromAPI(clip).xaudio; 59 if(isClipDirty) 60 { 61 isClipDirty = false; 62 HRESULT hr = sourceVoice.SetSourceSampleRate(clip.getSampleRate()); 63 ErrorHandler.assertLazyExit(SUCCEEDED(hr), 64 "Could not set source voice Sample Rate:\n\t"~HipXAudioPlayer.getError(hr)); 65 } 66 67 HRESULT hr = sourceVoice.SubmitSourceBuffer(buffer, null); 68 ErrorHandler.assertLazyExit(SUCCEEDED(hr), 69 "Could not submit XAudio2 source voice:\n\t"~HipXAudioPlayer.getError(hr)); 70 } 71 72 override IHipAudioClip clip(IHipAudioClip newClip) 73 { 74 if(newClip != clip) 75 isClipDirty = true; 76 super.clip(newClip); 77 return newClip; 78 } 79 80 alias loop = HipAudioSource.loop; 81 override bool loop(bool value) 82 { 83 bool ret = super.loop(value); 84 HipXAudioClip c = clip.getAudioClipBackend!(HipXAudioClip); 85 c.buffer.LoopCount = loop ? XAUDIO2_LOOP_INFINITE : 0; 86 return ret; 87 } 88 89 90 91 override bool play() 92 { 93 if(isPlaying) 94 { 95 sourceVoice.Stop(); 96 sourceVoice.FlushSourceBuffers(); 97 } 98 submitClip(); 99 100 HRESULT hr = sourceVoice.Start(0); 101 ErrorHandler.assertLazyExit(SUCCEEDED(hr), "XAudio2 Failed to play: \n\t"~HipXAudioPlayer.getError(hr)); 102 isPlaying = true; 103 return SUCCEEDED(hr); 104 } 105 override bool stop() 106 { 107 //May need to use XAUDIO2_PLAY_TAILS for outputting reverb too. 108 auto hr = sourceVoice.Stop(XAUDIO2_PLAY_TAILS); 109 ///Makes it return to 0 110 sourceVoice.FlushSourceBuffers(); 111 debug 112 ErrorHandler.assertLazyErrorMessage(SUCCEEDED(hr), "XAudio2 stop failure", HipXAudioPlayer.getError(hr)); 113 114 isPlaying = false; 115 return SUCCEEDED(hr); 116 } 117 override bool pause() 118 { 119 isPaused = true; 120 //May need to use XAUDIO2_PLAY_TAILS for outputting reverb too. 121 return SUCCEEDED(sourceVoice.Stop(XAUDIO2_PLAY_TAILS)); 122 } 123 override bool play_streamed() => false; 124 125 126 ~this() 127 { 128 if(sourceVoice !is null) 129 { 130 sourceVoice.DestroyVoice(); 131 sourceVoice = null; 132 } 133 } 134 }